﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ACS
{
    public class PcscUlc
    {
        public PcscUlc(PcscItem pcscConnection)
        {
            _PcscConnection = pcscConnection;
        }

        private PcscItem _PcscConnection;
        public PcscItem pcscConnection
        {
            get { return _PcscConnection; }
            set { _PcscConnection = value; }
        }

        private byte[] _cardSerial;
        public byte[] cardSerial
        {
            get
            {
                byte[] buffer = new byte[0];
                _cardSerial = new byte[7];

                buffer = readBinaryBlock(0, 8);

                Array.Copy(buffer, 0, _cardSerial, 0, 3);
                Array.Copy(buffer, 4, _cardSerial, 3, 4);

                _bbc0 = buffer[3];
                _bbc1 = buffer[8];

                return _cardSerial;
            }
        }

        private byte[] _otp;
        public byte[] opt
        {
            get 
            {
                _otp = readBinaryBlock(3, 4);
                return _otp; 
            }
            set 
            {
                if (value == null || value.Length != 4)
                    throw new Exception("Invalid data length");

                updateBinaryBlock(3, value);
            }
        }
        public int optInt
        {
            get { return Helper.byteToInt(opt); }
        }

        private byte? _bbc0 = null;
        public byte bbc0
        {
            get 
            {
                byte[] tmpByte;

                if (!_bbc0.HasValue)
                    tmpByte = cardSerial;

                return _bbc0.Value; 
            }
        }

        private byte? _bbc1 = null;
        public byte bbc1
        {
            get
            {
                byte[] tmpByte;

                if (!_bbc0.HasValue)
                    tmpByte = cardSerial;

                return _bbc1.Value;
            }
        }

        public byte[] readBinaryBlock(byte blockNum)
        {
            return readBinaryBlock(blockNum, 16);
        }

        public byte[] readBinaryBlock(byte blockNum, byte lengthToRead)
        {
            Apdu apdu;
            int returnCode;


            apdu = new Apdu();
            apdu.setCommand(new byte[] { 0xFF, 0xB0, 0x00, blockNum, lengthToRead });
            apdu.sendData = new byte[0];
            apdu.lengthExpected = lengthToRead + 2;

            returnCode = pcscConnection.sendApdu(ref apdu);
            if (returnCode != PCSCProvider.SCARD_S_SUCCESS)
                throw new PcscException(returnCode);

            if (apdu.sw[0] != 0x90)
                throw new Exception("read binary block failed");

            return apdu.receiveData.Take(lengthToRead).ToArray();
        }

        public int updateBinaryBlock(byte blockNum, byte[] data)
        {
            Apdu apdu;
            int returnCode;

            if (data.Length > 48)
                throw new Exception("Data has invalid length");

            if (blockNum == 2 || blockNum == 40)
                throw new Exception("Lock Page");

            apdu = new Apdu();
            apdu.setCommand(new byte[] { 0xFF, 0xD6, 0x00, blockNum, (byte)data.Length });
            apdu.lengthExpected = 2;

            apdu.sendData = new byte[data.Length];
            Array.Copy(data, apdu.sendData, data.Length);

            returnCode = pcscConnection.sendApdu(ref apdu);
            if (returnCode != PCSCProvider.SCARD_S_SUCCESS)
                throw new PcscException(returnCode);

            if (apdu.sw[0] != 0x90)
                return (int)MIFARE_ERROR_CODES.WRITE_BINARY_FAILED;

            return 0;
        }

        public string getErrorMessage(byte[] sw1sw2)
        {
            if (sw1sw2.Length < 2)
                return "Unknow Status Word (" + Helper.byteAsString(sw1sw2, false) + ")";

            else if (sw1sw2[0] == 0x63 && sw1sw2[1] == 0x00)
                return "Command failed";
            else
                return "Unknow Status Word (" + Helper.byteAsString(sw1sw2, false) + ")";
        }

        public static bool isUltralight(byte[] atr)
        {
            if (atr != null && atr.Length > 8 && Helper.byteArrayIsEqual(atr.Skip(4).Take(3).ToArray(), new byte[] { 0x80, 0x4F, 0x0C }))
                return true;
            else
                return false;
        }
    }
}
